package org.openstreetmap.josm.plugins.cadastre_es;

import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.plugins.cadastre_es.exceptions.CriticalException;

public class CadastreImage {
    private Tile [][] tiles;
    private double difLon;
    private double difLat;
    private static Bound<Double> bound = new Bound<Double>();
    private static int width;
    private static int height;
    private static int xTile;
    private static int yTile;
    private static int xTiles;
    private static int yTiles;
    public static final int PIXELS_EACH_DEGREE = 1500000;
    public static final double DEGREES_EACH_PIXEL = (double) 1 / PIXELS_EACH_DEGREE;
    
    public CadastreImage(LatLon start, LatLon end) throws CriticalException {
        bound.left = start.getX();
        bound.right = end.getX();
        bound.bottom = start.getY();
        bound.top = end.getY();
        difLon = Math.abs(bound.right - bound.left);
        difLat = Math.abs(bound.top - bound.bottom);
        height = (int) (difLat * PIXELS_EACH_DEGREE);
        width = (int) (difLon * PIXELS_EACH_DEGREE);
        xTile = -1;
        yTile = -1;
        xTiles = 1;
        yTiles = 1;
        Pixel.img = this;
        initTiles();
    }
    
    private void initTiles() {
        xTiles = (int) Math.ceil((((double) width) / ((double) Tile.MAX_SIZE)));
        yTiles = (int) Math.ceil((((double) height) / ((double) Tile.MAX_SIZE)));
        tiles = new Tile[xTiles][];
        for (int x = 0; x < xTiles; x++) {
        	tiles[x] = new Tile[yTiles];
            for (int y = 0; y < yTiles; y++)
                tiles[x][y] = new Tile(x, y);
        }
    }
    
    public Tile changeToNextTile() throws CriticalException {
		if (Process.DEBUG && xTile > -1 && yTile > -1)
			tiles[xTile][yTile].saveToDisk();
		Tile.clean(xTile, yTile);
    	xTile = (xTile + 1) % xTiles;
    	if (xTile == 0) {
    		yTile = yTile + 1;
    		if (yTile >= yTiles) {
    			return null;
    		}
    	}
    	return tiles[xTile][yTile];
    }
    
    public int getRGB(Pixel p) throws CriticalException {
    	if (outside(p))
    		return Pixel.WHITE;
    	int xTile = (int) Math.ceil(p.x() / Tile.MAX_SIZE);
    	int yTile = (int) Math.ceil(p.y() / Tile.MAX_SIZE);
    	if (tiles[xTile][yTile].isEmpty())
    		return Pixel.WHITE;
	    if (xTile != CadastreImage.xTile || yTile != CadastreImage.yTile)
	    	tiles[xTile][yTile].load();
	    int x = p.x() % Tile.MAX_SIZE;
	    int y = p.y() % Tile.MAX_SIZE;
	    return tiles[xTile][yTile].getRGB(x, y);
    }
    
    public void setRGB(Pixel p, int rgb) throws CriticalException {
    	if (outside(p)) {
    		System.out.println("WARNING: setRGB() to an outside pixel " +p.x()+","+p.y());
    		return;
    	}
    	int xTile = (int) Math.ceil(p.x() / Tile.MAX_SIZE);
    	int yTile = (int) Math.ceil(p.y() / Tile.MAX_SIZE);
    	if (tiles[xTile][yTile].isEmpty()) {
    		System.out.println("WARNING: setRGB() to an empty tile " +p.x()+","+p.y());
    		return;
    	}
    	if (xTile != CadastreImage.xTile || yTile != CadastreImage.yTile)
    		tiles[xTile][yTile].load();
    	int x = p.x() % Tile.MAX_SIZE;
    	int y = p.y() % Tile.MAX_SIZE;
    	tiles[xTile][yTile].setRGB(x, y, rgb);
    }
    
    private boolean outside(Pixel p) {
    	return p.x() >= width || p.y() >= height || p.x() < 0 || p.y() < 0;
    }
    
    public static int getWidth() { return width; }    
    public static int getHeight() { return height; }
    public static Bound<Double> getLatLonBound() { return new Bound<Double>(bound); }
    public static int getXtiles() { return xTiles; }
    public static int getYtiles() { return yTiles; }
}